var $_grid;
$.dashboard_init = function(widget_registry,host)
{
	$('#dashboard-embed').dialog({
		'autoOpen':false,
		'title': 'Embed Code',
		'width': 500,
		'height': 200,
		'resizable' : false,
		'modal': true,
		'draggable': false
	})
	$('.dashboard-embed').click(function() {
		$('#dashboard-embed').dialog('open');
	})

  /* organize widget definitions */
  $.widget_registry =  {
    data: widget_registry,
    get_by_key: function(t) {
      for (var i=0; i<this.data.length; i++)
        if (this.data[i].key_name==t)
          return this.data[i]
      return null;
    }
  };
  $( "#widget-chooser" ).widget_selector();
  var $grid = $( "#grid" ).widget_grid({
    widget_registry: widget_registry,
    widget_draggables: $( ".widget-def" )
  });
  $_grid = $grid; // for global access
  
  /* get all connections for user */
  $.get_json('/api/connections/list', null, function(conn_data)
  {
	$("#connection-count").text( conn_data.length );
    $.connections = {
      data: conn_data,
      get_by_type: function(t)
      {
        //connection string may have scopes at the end
        var conns = [];
        for (var i=0; i<this.data.length; i++)
          if (this.data[i].type==t)
            conns.push(this.data[i]);
        return conns;
      },
      add_connection: function(conn)
      {
        for (var i=0; i<this.data.length; i++)
          if (this.data[i].key == conn.key)
          {
            this.data[i] = conn;
            return;
          }
        this.data.push(conn);
      }
    };

    //manage selected dashboard
    var _sel_index = -1;
    var _dshbrd_data = [];
    var current_db = function()
    {
    	return _dshbrd_data[_sel_index];
    };

    //dashboard props : tags, public, embed
    //TODO move the following info a dashboard_props class...
	var tagsavef = function(tags)
	{
		 var dash = _dshbrd_data[_sel_index];
		 $.get_json('/api/dashboard/tags/'+dash.key+'/'+tags, null, function(dshbrd_data)
	     {
			 _dshbrd_data[_sel_index] = dshbrd_data;
	     });
	};

	var publicsavef = function(b)
	{
		 var dash = _dshbrd_data[_sel_index];
		 $.get_json('/api/dashboard/public/'+dash.key+'/'+b, null, function(dshbrd_data)
	     {
			 _dshbrd_data[_sel_index] = dshbrd_data;
	     });
	};
	var $dbx = $('#dashboard-export');
	$dbx.click(function(event){
		location.href = '/api/dashboard/export/'+current_db().key;
	});

    var $dbt = $('#dashboard-tags-value');
	$dbt.keypress(function(event)
    {
	   if (event.which == '13')
	   {
	     event.preventDefault();
	     tagsavef($dbt.val());
	   }

	});
	$dbt.blur(function(event)
    {
	  // if (dirty)//TODO
	     tagsavef($dbt.val());
	});

    var $dbp = $('#dashboard-public-value');
    $dbp.change(function(event)
    {
		publicsavef($dbp.val())
	});

	var update_props = function(d)
    {
    	var s = '<iframe src="http://'+host+'/dashboard-embed?p='+d.key+'"'+
    		    ' scrolling="no" frameborder="0" marginwidth="0" marginheight="0"'+
    		    ' width="300" height="300"></iframe>';
        $('#dashboard-embed-code').val(s)

    	//$dbt.val(d.tags);
        //$dbp.val(d['public'] ? "yes" : "no")

        //$.share_init(d.key);
    };

    // list dashboards, set up a tab for each as well as the grid w/ default dashbaord
    $.get_json('/api/dashboard/list', null, function(dshbrd_data)
    {
      _dshbrd_data = dshbrd_data;
      var $dashboard_tabs = $('#dashboards').dashboard_tabs({
          select: function(e,ui)
          {
        	_sel_index = ui.index;
            $grid.widget_grid('load', current_db().key);
            $.cookie('dashboard-tab-pref-idx',_sel_index);
            update_props(current_db());
          }
      });
      $dashboard_tabs.dashboard_tabs('value', dshbrd_data);
      if (dshbrd_data.length!=0)
      {
        var tab_pref_idx = $.cookie('dashboard-tab-pref-idx');
        if (tab_pref_idx == null || isNaN(tab_pref_idx)) tab_pref_idx = 0;
        if (tab_pref_idx>dshbrd_data.length-1) tab_pref_idx = 0;
        logging.log('selecting dashboard tab '+tab_pref_idx+' of '+dshbrd_data.length)
        _sel_index = tab_pref_idx;
        $grid.widget_grid('load', current_db().key);
        $dashboard_tabs.dashboard_tabs('select',_sel_index);
        update_props(current_db());
      }

      $("#widget-category-bg").css('opacity',.5);
    });
  });
}




$.dashboard_embed_init = function(dashboard,widg_insts)
{
    var $d = $('#d').dashboard_embed({dashboard: dashboard, widgets: widg_insts});
}






/**
 * The dasboard widget grid is width x height dimensions with each cell being cell_size.
 * There is equal padding of cell_padd pixels to the right and below each cell except the last.
 * There are 3 root containers: loading state, background, widgets (_$loading,_$bg,_$children)
 * Use 'setWidgets' to register a list of draggable widget <em>definitions</em> with the grid.
 * Use 'load' to restore widgets. Save is automatic and occurs on drop, reposition and resize.
 */

 /**
  * TODO
  * 1. dragging off grid... visual feedback?
  * 2. dropping off grid - snap back to original position
  *
  */
$.widget( "impact.widget_grid",
{
  options: { widget_registry: [], widget_draggables: [] },

  _create: function()
  {
    var self = this, el = this.element;

    self.width      = 4;
    self.height     = 4;
    self.cell_size  = 228;
    self.cell_pad   = 12;

    el.css({'position':'relative'});

    self._$loading = $("<div>Loading...</div>").css({'position':'absolute', 'top':0, 'left': 0});
    self._$loading.hide();
    el.append(self._$loading);

    self._init_bg();
    el.append(self._$bg);

    self._$children = $("<div></div>").css({'position':'absolute', 'top':0, 'left': 0});
    el.append(self._$children);

    el.droppable({
      drop: function( event, ui )
      {
        //var offset = self._get_woffset();
        //var pc = self.cell_size + self.cell_pad;
    	//var r = Math.round(top/pc);
    	//var c = Math.round(left/pc);
        //calc row column
        //find out if something is in the row column
        // if correllation widget is there and in config mode
        // -- process correlation drop
        var info = self._get_cell_dd_event_info(ui);
        if(ui.draggable.hasClass("widget-def"))
        {
          return self._process_widget_drop(info,ui);
        }
        else if(ui.draggable.hasClass("widget-grid-cell"))
        {
          //logging.log(event,ui)
          return self._process_instance_reposition(info,ui);
        }
        else if(ui.draggable.hasClass("widget-corr-member"))
        {
          return self._process_remove_from_correlation(info,ui);
        }
      }
    });

    self.setWidgets(self.options.widget_draggables)
  },

  _destroy: function()
  {
    var self = this, el = this.element;
  },


  _get_woffset: function(){
    var self = this, el = this.element;
    return {top: el.offset().top , left: el.offset().left };
  },


  /*  add draggable to a list of widget definitions
   *  each widget definition element must have the id widget-{{dbkey}}
   *    param widgets the list of page elements
   */
  setWidgets: function(widgets)
  {
    var self = this, el = this.element;
    widgets.each(function(i,el){
      $(el).draggable({
        opacity:  0.9,
        helper:   "clone",
        scrollSensitivity: 50,
        cursorAt: { left: -5,top:-5 },
        appendTo: 'body' ,
      	drag:     function(event, ui) {
        	self._process_widget_over(ui);
        },
        zIndex:   9999
      });
      $(el).css({cursor:'pointer'})
    });
  },

  /* set up a grid from a dashboard key */
  load: function(dashboard_key)
  {
    var self = this, el = this.element;
    if (self.dashboard_key == dashboard_key)
      return;
    self.dashboard_key = dashboard_key;
    self._$loading.show();
    self._$bg.hide();
    self._$children.hide();
    self._over_bg_cell();//resets the grid rollover hilight state
    var self = this, el = this.element;
    self._$children.empty();
    $.get_json('/api/dashboard/get_widgets/'+dashboard_key, null, function(data) {
      self._$loading.hide();
      self._$bg.show();
      self._$children.show();
      for (var i=0; i<data.length; i++)
      {
        var wi = data[i]
        var $cell = self._add_cell(wi.widget.key_name, wi.rect[1], wi.rect[0], wi.rect[2], wi.rect[3]);
        $cell.widget_grid_cell('data',wi);
      }
    });
  },
  
  convertToCorrelation: function(wid,type)
  {
	var self = this;
	var $cell = self._get_cell_by_id(wid);
	var wi = $cell.widget_grid_cell('data');
	$.get_json('/api/dashboard/convert_to_correlation/'+wid+'/'+type, null, function(corr_wi)
	{
		  $cell.remove();
		  $cell = self._add_cell('impact_correlation', wi.rect[1], wi.rect[0], wi.rect[2], wi.rect[3])
	      $cell.widget_grid_cell('data',corr_wi);
	});
  },
  
  updateProps: function(wid,v)
  {
	var self = this;
	var $cell = self._get_cell_by_id(wid);
	$cell.widget_grid_cell('updateProps',v);
  },
  
  _get_cell_dd_event_info: function(ui)
	{
		var self = this, el = this.element;
		var p    = $.globalToLocal(el,ui.offset.left,ui.offset.top)
  		var top  = p.y
  		var left = p.x
    	var pc = self.cell_size + self.cell_pad;
    	var r = Math.round(top/pc);
    	var c = Math.round(left/pc);
    	return {row:r,col:c,top:top,left:left,pc:pc}
	},
  _process_widget_over: function(ui)
  {
	var self = this, el = this.element;
	var p    = $.globalToLocal(el,ui.offset.left,ui.offset.top)
  	var top  = p.y
  	var left = p.x
    var pc = self.cell_size + self.cell_pad;
    var r = Math.round(top/pc);
    var c = Math.round(left/pc);

    if (r<0 || c<0 || self._$bg_els[r][c] == null)
      return;
    //logging.info('def over...',self._$bg_els[r][c]);
    self._over_bg_cell(r,c);
  },
	//def drop
  _process_widget_drop: function(info,ui)
  {
    var self = this, el = this.element;
    var r = info.row;
    var c = info.col;
    self._over_bg_cell();
    if (r<0 || c<0 || self._$bg_els[r][c] == null)
      return false;
    var widget = ui.helper;
    logging.log('def dropped...'+info.top+" "+info.left,widget.attr('id'));
    var type = widget.attr('id').substring(7);
    var $cell = self._add_cell(type,r,c)
    $.get_json('/api/dashboard/add_widget/'+self.dashboard_key+'/'+type+'/'+c+'/'+r, null, function(data) {
      logging.log('widget instance saved')
      $cell.widget_grid_cell('data',data);
    });
  },

  _process_instance_over: function(ui)
  {

	var self = this, el = this.element;
  	var info = self._get_cell_dd_event_info(ui)
    var r = info.row;
  	var c = info.col;
  	var cell = ui.helper;

    cell.css('zIndex', 9999);

    if (r<0 || c<0 || self._$bg_els[r]==null || self._$bg_els[r][c] == null)
      return false;
    //logging.log('inst over...'+top+" "+r+" "+c,cell.attr('id'));
    self._over_bg_cell(r,c);
  },

  _process_instance_reposition: function(info,ui)
  {
    var self = this, el = this.element;
    var r = info.row;
    var c = info.col;
    var cell = $(ui.draggable);
    self._over_bg_cell();
    if (r<0 || c<0 || self._$bg_els[r] == null || self._$bg_els[r][c] == null)
      return false;
    logging.log('inst repositioned...'+info.top+" "+info.left,cell);

	var intersect = self._get_intersecting_cells(c,r,1,1,cell.widget_grid_cell('data').key);
    if(intersect)
    {
		var def = intersect.cell.widget_grid_cell('definition');
		if(def.is_correlation)
    	{
			cell.hide();
			var args = intersect.cell.widget_grid_cell('key')+'/'+cell.widget_grid_cell('key');
			$.get_json('/api/dashboard/add_widget_to_correlation/'+args, null,
			 function(data) {
				intersect.cell.widget_grid_cell('data',data);
				cell.remove();
				return;
    		 },function(e)
    		 {
    			 cell.show();
    			 alert(e);
    		 });
    	}
    	else
    		cell.css('zIndex',parseInt(intersect.cell.css('zIndex'))+1);
    }
    else
    	cell.css('zIndex',10);
    cell.animate({'top':r*info.pc,'left':c*info.pc},100);
    $.get_json('/api/dashboard/update_widget_rect/'+cell.widget_grid_cell('key'), { x: c, y: r }, function(data) { /*  +'?x='+c+'&y='+r  */
        cell.widget_grid_cell('value',data)
    	logging.log('widget xy saved',data)
    });
  },
  _process_remove_from_correlation: function(info,ui)
  {
	    var self = this, el = this.element;
    	var r 			= info.row;
    	var c 	   		= info.col;
    	var draggable   = $(ui.draggable);
    	var $corr_cell 	= $(ui.draggable.data('corr_cell'));

    	var corr_key 	= draggable.data('corr_key');
    	var member_key 	= draggable.data('wi_key');
  		var intersect 	= self._get_intersecting_cells(c,r,1,1);

  		if(intersect)
    	{
			var def = intersect.cell.widget_grid_cell('definition');
			if(def.is_correlation )
	    	{
				return false; //cant drag from correlation to correlation yet//
	    	}
    	}

		 $.get_json('/api/dashboard/remove_widget_from_correlation/'+corr_key+"/"+member_key, { x: c, y: r,w:1,h:1 }, function(data) {

			 	 var $cell = self._add_cell(data.wi.widget.key_name, data.wi.rect[1], data.wi.rect[0], data.wi.rect[2], data.wi.rect[3]);
    			 if(intersect)
    			 	$cell.css('zIndex',parseInt(intersect.cell.css('zIndex'))+1);

    			 $cell.widget_grid_cell('data',data.wi);
    			 $corr_cell.widget_grid_cell('data',data.corr);

			 });
  },
  _process_instance_resize_start: function(width,height,cell)
  {
    var self = this, el = this.element;
    //self._resize_init = { width: width, height: height }

  },

  _process_instance_resizing: function(width,height,$cell)
  {
    var self = this, el = this.element;
    //logging.log('inst resizing...'+width+" "+height,$cell);
    //$cell.widget_grid_cell('size',[width,height]);
  },

  _process_instance_resize_stop: function(width,height,$cell)
  {
    var self = this, el = this.element;
    var p = self.cell_pad;
    var pc = self.cell_size + p;
    var r = Math.round(width/pc);
    var c = Math.round(height/pc);
    if (r<1)
      r = 1;
    if (c<1)
      c = 1;
    //logging.log('inst resized...'+r+" "+c,cell.name);
    $cell.animate({'width':r*pc-p,'height':c*pc-p},40);
    $cell.widget_grid_cell('size',[r*pc-p,c*pc-p]);
    var gx = $cell.widget_grid_cell('data').rect[0];
    var gy = $cell.widget_grid_cell('data').rect[1];
    var intersect = self._get_intersecting_cells(gx,gy,gx+r,gy+c,$cell.widget_grid_cell('data').key);
    if(intersect)
    {
    	//alert('intersect on resize'+intersect.wi.widget.name+" "+intersect.wi.key+" "+intersect.cell.css('zIndex'))
  		$cell.css('zIndex',parseInt(intersect.cell.css('zIndex'))+1);
    }

    $.get_json('/api/dashboard/update_widget_rect/'+$cell.widget_grid_cell('key'), { h: c, w: r }, function(data) { /* +'?h='+c+'&w='+r */
      	$cell.widget_grid_cell('value',data)
    	logging.log('widget wh saved',data)
    });
  },

  _on_delete_cell: function($cell)
  {
    var self = this, el = this.element;
    $.get_json('/api/dashboard/delete_widget/'+$cell.widget_grid_cell('key'), null, function(data) {
      logging.log('widget wh deleted',data)
      $cell.remove();
    });
  },

  toString: function()
  {
    return 'widget_grid';
  },

  _add_cell: function(widget_type,r,c,w,h)
  {
    var self = this, el = this.element;
    if (w==null) w=1;
    if (h==null) h=1;
    var p = self.cell_pad;
    var pc = self.cell_size + p;
    var $cell = $("<div></div>").widget_grid_cell({
      is_editable: true,
      deletef: function(){ self._on_delete_cell($cell); },
      width:(pc*w-p), height: (pc*h-p)
    });
    $cell.widget_grid_cell('dashboard',self);
    $cell.attr('id', widget_type);
    $cell.draggable({
      handle: '.widget-drag-handle',
      drag: function(event, ui) { self._process_instance_over(ui); },
      containment:'#grid'
    });
    $cell.resizable({
      start: function(event, ui) { self._process_instance_resize_start(ui.size.width,ui.size.height,ui.element); },
      resize: function(event, ui) { self._process_instance_resizing(ui.size.width,ui.size.height,ui.element); },
      stop: function(event, ui) { self._process_instance_resize_stop(ui.size.width,ui.size.height,ui.element); },
      helper: "ui-resizable-helper"
    });
    $cell.css({
      'width':  (pc*w-p)+'px',
      'height': (pc*h-p)+'px',
      'top':    (pc*r)+'px',
      'left':   (pc*c)+'px',
      'position': 'absolute',

    });
	var intersect = self._get_intersecting_cells(c,r,w,h)
    if(intersect)
		$cell.css('zIndex',parseInt(intersect.cell.css('zIndex'))+1);
    else
    	$cell.css('zIndex',10);

    self._$children.append($cell);
    return $cell;
  },
   _get_intersecting_cells:function(x,y,w,h,exclude_key)
	{
	  var self = this, el = this.element;
	  var cc = self._$children.children();
	  var isects = []
	  for(var i = 0;i < cc.length;i++)
	  {

		  var cell = $(cc[i]);

		  var wi   = cell.widget_grid_cell('data');
		  if(cell.widget_grid_cell('in_edit_mode'))
		  {
			  //in edit mode we konw that the editor is 2 cells high
			  wi.rect[3] = 2;
		  }

		  if(exclude_key)
			  if (wi.key == exclude_key)
			  	continue;
		  var r1 = [x,y,x+w,y+h]
		  var r2 = [wi.rect[0],wi.rect[1],wi.rect[0]+wi.rect[2],wi.rect[1]+wi.rect[3]]

		  //console.log('rect 1 '+r1[0]+' '+r1[1]+' '+r1[2]+' '+r1[3])
		   //console.log('rect 2 '+wi.widget.name+' '+r2[0]+' '+r2[1]+' '+r2[2]+' '+r2[3])
		  /* left,top,right,bottom*/
	   if(!( r2[0] >= r1[2]
		|| r2[2] <= r1[0]
		|| r2[1] >= r1[3]
		|| r2[3] <= r1[1]
		))
		{


		   isects.push({cell:cell,wi:wi})
		}
	  }
	  if(isects.length > 0)
	  {
	  	isects.sort(function (a, b){
				return (parseInt(b.cell.css('zIndex')) - parseInt(a.cell.css('zIndex'))) //causes an array to be descending by zIndex
	  	});
	  	return isects[0]
	  }
	  return null;
	},
  _get_cell_by_id: function(key)
  {
	  var self = this;
	  var cc = self._$children.children();
	  for(var i = 0;i < cc.length;i++)
	  {
		  var $cell = $(cc[i]);
		  var wi   = $cell.widget_grid_cell('data');
		  if (wi.key==key)
			  return $cell;
	  }
	  return null;
  },
  _init_bg: function()
  {
    var self = this, el = this.element;
    var pc = self.cell_size + self.cell_pad;
    self._$bg = $('<div></div>').css({'position':'absolute', 'top':0, 'left': 0});
    self._$bg_els = [];
    for (var i=0; i<self.height; i++)
    {
      var row = $('<div></div>')
                .attr('id','__wr'+i)
                .css({'position':'absolute', 'height':self.cell_size+'px', 'top': (i*pc)+'px'});
      self._$bg_els[i] = [];
      for (var j=0; j<self.width; j++)
      {
         var col = $('<div></div>')
                .attr('id','__wc'+i+'_'+j)
                .css({'position':'absolute', 'width':self.cell_size+'px','height':self.cell_size+'px', 'left':j*pc})
                .addClass('rounded-corners bgcell');
         self._$bg_els[i][j] = col;
         row.append(col);
      }
      self._$bg.append(row);
    }
  },

  _over_bg_cell: function(r,c)
  {
    var self = this, el = this.element;
    var ccell = r==null || c==null ? null : self._$bg_els[r][c];
    if (self._last_bg_cell == ccell)
      return;
    if (ccell != null)
      ccell.addClass('ui-over');
    if (self._last_bg_cell != null)
      self._last_bg_cell.removeClass("ui-over");
    self._last_bg_cell = ccell;
  }

});





/**
 * display grid
 */



$.widget( "impact.dashboard_embed",
{

  options: { dashboard: null, widgets: [] },
  _create: function()
  {
    var self = this, el = this.element;

    var max_x = 0;
    var max_y = 0;

    for (var i=0; i<self.options.widgets.length; i++)
    {
	    var wi = self.options.widgets[i];
	    max_x = Math.max(max_x, wi.rect[0]+ wi.rect[2]);
	    max_y = Math.max(max_x, wi.rect[0]+ wi.rect[2]);
    }
    var s = Math.min( $(document).width()/max_x, $(document).height()/max_y );
    self.cell_size  = Math.floor(s-15);//235;
    self.cell_pad   = 15;

    el.css({'position':'relative'});

    self._$children = $("<div></div>").css({'position':'absolute', 'top':0, 'left': 0});
    el.append(self._$children);

	for (var i=0; i<self.options.widgets.length; i++)
	  {
	    var wi = self.options.widgets[i]
	    var $cell = self._add_cell(wi.widget.key_name, wi.rect[1], wi.rect[0], wi.rect[2], wi.rect[3]);
	    $cell.widget_grid_cell('data',wi);
	  }

  },


  _add_cell: function(widget_type,r,c,w,h)
  {
    var self = this, el = this.element;
    if (w==null) w=1;
    if (h==null) h=1;
    var p = self.cell_pad;
    var pc = self.cell_size + p;
    var $cell = $("<div></div>").widget_grid_cell({
      is_editable: false,
      width:(pc*w-p),
      height: (pc*h-p)
    });
    $cell.attr('id', widget_type);

    $cell.css({
      'width':  (pc*w-p)+'px',
      'height': (pc*h-p)+'px',
      'top':    (pc*r)+'px',
      'left':   (pc*c)+'px',
      'position': 'absolute'
      });

    self._$children.append($cell);
    return $cell;
  }

});



 /**
  * the individual widget grid cell item!
  */

 var WIDGET_STATE_INIT                			= 0
 var WIDGET_STATE_REQUIRES_AUTH           		= 1
 var WIDGET_STATE_REQUIRES_SERVICE_SELECTION    = 2
 var WIDGET_STATE_READY               			= 3
 var WIDGET_SERVICE_STATE_OK            		= 4
 var WIDGET_SERVICE_STATE_FAILED_500        	= 500
 var WIDGET_SERVICE_STATE_FAILED_NEED_AUTH      = 501
 var WIDGET_SERVICE_STATE_FAILED_NEED_SELECTION = 502
 var WIDGET_SERVICE_STATE_FAILED_DATA_ERROR     = 503
 var WIDGET_ERROR_NO_DEF              			= 900


 var WIDGET_STATE_CORRELATION_REQUIRES_MEMBERS	= 620
 var WIDGET_STATE_CORRELATION_READY				= 621
 var WIDGET_STATE_CORRELATION_OK				= 622
 var WIDGET_STATE_CORRELATION_ERROR				= 623
 var WIDGET_STATE_CORRELATION_FAILED_500		= 624

 function widget_state_to_str(s)
 {
    switch(s)
    {
    case WIDGET_STATE_INIT:
      return "Initialized"
    case WIDGET_STATE_REQUIRES_AUTH:
      return "Requires Connection"
    case WIDGET_STATE_REQUIRES_SERVICE_SELECTION:
      return "Requires Selector"
    case WIDGET_STATE_READY:
      return "Ready For Data"
    case WIDGET_SERVICE_STATE_OK:
      return "Got Data OK"
    case WIDGET_SERVICE_STATE_FAILED_500:
    case WIDGET_SERVICE_STATE_FAILED_DATA_ERROR:
      return "Data Service Failed - Try Again Later"
    case WIDGET_SERVICE_STATE_FAILED_NEED_AUTH:
      return "Data Service Failed - Connection Is Invalid. Reconnect."
    case WIDGET_ERROR_NO_DEF:
      return "Unable To Find Widget Definition"
    case WIDGET_STATE_CORRELATION_READY:
    	return "Correlation Ready"
    case WIDGET_STATE_CORRELATION_OK:
    	return "Correlation OK"
    case WIDGET_STATE_CORRELATION_ERROR:
    	return "Correlation ERROR"
    case WIDGET_STATE_CORRELATION_FAILED_500:
		return "Correlation Failed 500"
    case WIDGET_STATE_CORRELATION_REQUIRES_MEMBERS:
      	return "Correlation Requires Members"
    default:
      return "Unknown State - "+s
    }
}

WCELLID = 57489754;
$.widget( "impact.widget_grid_cell",
{
  options: {width: 50, height: 50, is_editable: false },

  _create: function()
  {
    var self = this, el = this.element;

    el.css('position','absolute');
    el.addClass('widget-grid-cell');

    /* front side of widget */
    self._$front = $("<div></div>");
    self._$front.css({ '-webkit-backface-visibility': 'hidden'})
    el.append(self._$front);

    /* element content */
    self._$c = $$$(self._$front, null, 20, 10).css('overflow','hidden');

    /* raphael content */
    WCELLID++;
    self.pid = "__wc"+WCELLID;
    self._$p = $$$(self._$front, null, 30, 10).attr('id',self.pid);

    /* drag handle */
    self._$drag_handle = $$$(self._$front, 'widget-drag-handle');
	self._$drag_handle.css({cursor:'pointer'})
    /* title */
    self._$title = $$$(self._$front, 'widget-title', 26, null, null, 10);

    /* bottom part */
    self._$action_bar = $$$(self._$front, 'widget-action-bar', null, null, 26, null);
    self._$action_text = $$$(self._$action_bar, 'widget-action-text');
    if (self.options.is_editable)
      self._$edit_widget = $$$(self._$action_bar, 'widget-edit-button', 10, 15, null, null, function(){  self._enter_config(); });


    /* add the edit/style panel if necessary */
    if (self.options.is_editable)
    {
      /* back side */
      self._$back = $$$(el, 'widget-props-panel');
     //// self._$back.css({ '-webkit-transform': 'rotateY(-180deg)' })
	  self._$back.css('height',self.options.height+'px');
	  self._$back.hide();

      self._$props = $$div(self._$back, 'widget-props');
      /*containers*/
      self._$state                = $$div(self._$props, 'widget-state');
      self._$connection_container = $$div(self._$props, 'widget-connection-container');
      self._$selector_container   = $$div(self._$props, 'widget-selector-container');
	  self._$corr_members_container = $$div(self._$props, 'widget-corr-members-container');
      self._$props_container      = $$div(self._$props, 'widget-props-container');

      /* delete button */
      self._$close = $$$(self._$back, 'widget-delete-button', 10, 10, null, null, function(){  self._trigger( "deletef" ); });

    }

    self._$network = $("<div>NETWORK STATUS THING</div>");
    self._$network.hide();
    el.append(self._$network)

    self.update_size();
  },


  _destroy: function()
  {
    var self = this, el = this.element;
    el.empty();
  },

  _add_connection_props:function(has_connections_for_type)
  {
      var self = this;

      if (!self.options.is_editable)
    	return;
      self._$connection_container.empty()

      self.connection_descriptors   = self.definition().required_connection_descriptors;
      self.connections        = {}
      self._$connection_selects   = []
      self._$add_conn_buttons     = []

      for(var i = 0;i < self.connection_descriptors.length;i++)
      {
        var descriptor = self.connection_descriptors[i]
        self.connections[descriptor.name] = $.connections.get_by_type(descriptor.type);
        if(self.connections[descriptor.name].length != 0)//only draw select if there is something to select
        {
          self._$connection_selects[i] = $$select(self._$connection_container,
        		  				{idx:new String(i)},function(e){self._update_connection(e)});
          for (var ii=0; ii<self.connections[descriptor.name].length; ii++)
           {
             var conn = self.connections[descriptor.name][ii];
             var selected = self._data.connections[descriptor.name] != null &&
             				conn.key == self._data.connections[descriptor.name].key;
             self._$connection_selects[i].add_option(conn.username,selected,conn.key);
           }
        }

      self._$add_conn_buttons[i] = $$button(self._$connection_container,'Add',{idx:new String(i)},function(e){
      create_authorization(self.key(),e.data.idx, function(conn){
          $.connections.add_connection(conn);
          $.get_json('/api/dashboard/sync_widget/'+self._data.key, null, function(wi){
            self.data(wi);
          });
        });
      });

     $$br(self._$connection_container);
      }


  },
  _add_selector_props:function()
  {
    var self = this;
    if (!self.options.is_editable)
    	return;

    self._$selector_container.empty();

    var selector_names = self.definition().service_selector_names;
    var selector_types = self.definition().service_selector_types;

      self._$selectors = {}
    for(var i = 0;i < selector_names.length;i++)
      {
    	var stype = 'select';
    	if (selector_types.length>i)
    		stype = selector_types[i];
    	switch (stype)
    	{
	    	case 'select':
		        self._$selectors[selector_names[i]] = $$select(self._$selector_container,
		        		{selector_name:new String(selector_names[i])}, function(e){self._update_selector(e.data.selector_name)});
		
		        self._$selectors[selector_names[i]].add_option("Loading "+selector_names[i]+" Selector...");
		        $.get_json('/api/dashboard/get_selector_data_for_widget/'+self.key()+"/"+selector_names[i], null, function(obj)
		            {
		
		                var selector_name       = obj.selector_name;
		                var selector_display_vals   = obj.display_values;
		                var selector_vals       = obj.values;
		                var selector    = self._$selectors[selector_name]
		                selector.empty()
		                selector.add_option('Choose...',false,selector_vals[0]);
		                for(var ii = 0;ii < selector_vals.length;ii++)
		                {
		                      var selected = self._data.selector_values[selector_name] != null &&
		                      				 self._data.selector_values[selector_name] == selector_vals[ii];
		                      selector.add_option(selector_display_vals[ii],selected,selector_vals[ii]);
		                }
		            });
		        break;
	    	case 'input':
	    		var input = $$input(self._$selector_container, 30);
	    		input.change(selector_names[i], function(e){
	    			self._update_selector(e.data)
	    		});
	    		input.val(self._data.selector_values[selector_names[i]]);
	    		self._$selectors[selector_names[i]] = input;
	    		break;
    	}
        $$br(self._$selector_container);
      }
  },
  _add_correlation_members:function()
  {
	  var self = this;

	  self._$corr_members_container.empty()

	  self._$corr_members = []
	  var l = $$label(self._$corr_members_container,'<b>Correlation Members:</b>');
	  l.css({'height':'245px','position':'relative'})

	  var slug_layer = $$div(l);
	  slug_layer.css({'position':'absolute','zIndex':'1','top':'18px'});
	  for(var i = 0;i < 4;i++)
	  {
		   var s = $$correlation_slug('widget-corr-slug');
 			s.css('float','left');
		   slug_layer.append(s);
	  }

	  var member_layer = $$div(l)
	  member_layer.css({'position':'absolute','zIndex':'2','top':'18px'});
	  var cc = self.data().children;
	  for(var i = 0;i < cc.length;i++)
	  {

		 //var m = $$label(self._$corr_members_container,cc[i].widget.key_name+' '+widget_state_to_str(cc[i].state),'widget-corr-member');
		 var m = $$correlation_member(cc[i].widget.key_name+'<br/>'+widget_state_to_str(cc[i].state),'widget-corr-member');
		 m.css('float','left');
		 member_layer.append(m);

		 m.data({corr_cell:self.element,
			 	 corr_key:self.data().key,
			 	 wi_key:cc[i].key});

		 m.draggable({
       	 	opacity:  0.9,
        	helper:   "clone",
        	scrollSensitivity: 50,
        	appendTo: 'body' ,
      		drag:     function(event, ui) {
        		self.dashboard()._process_widget_over(ui);
        },
        zIndex:   9999,
        containment:'#grid'
      });
		m.css({cursor:'pointer'})

		 self._$corr_members[i] = m;
	  }
	  slug_layer.append($$clear_both);
	  member_layer.append($$clear_both);
  },
  _add_done_button:function()
  {
    var self = this;
    if(self._$done != null)
    	self._$done.remove();
    self._$done = $$$(self._$back, 'widget-done-button', null, 10, 10, null, function(){ self._exit_config(); });

  },
  _add_props_form:function()
  {
    var self = this;
    if (!self.options.is_editable)
    	return;

    self._$props_container.empty()
    self._$props_container.append("loading props...");
    $.get('/api/dashboard/get_props_editor/'+self.key(), function(data) {
      self._$props_container.empty()
      self._$props_container.append(data)

      /* i modified jquery.field.js formHash so that it will trigger the change event when
       * it sets a value on a select. This lets us hook into that change event
       * in our  django property editor templates so that we can show hide property panels
       * easily
       */
      $('#'+self.key()+'-props-form').formHash(self.data().props);
      self.updateProps();

    });
  },
  _add_renderer:function()
  {
    var self = this;
    if (self.renderer==null)
      {
      var t = self.data().widget.key_name;
      // create new widget renderer
        var f = window.impact.widgets[t];
        if (f != null)
        {
          self.renderer = new f();
          $.extend(self.renderer,{
	    	  setValue: function(ctx)
	          {
	              self.renderer.data 		   = ctx.data
	              self.renderer.props 		   = ctx.props
	              self.renderer.connections 	= ctx.connections
	              self.renderer.selector_values = ctx.selector_values
	              self.renderer.selector_display_values = ctx.selector_display_values
	          },
              setSize: function(width,height)
              {
	              self.renderer.width = width;
	              self.renderer.height = height;
	              if (self.renderer.render!=null)
	            	  self.renderer.render();
              }
          });
          self._$widget_renderer = Raphael(self.pid,self.options.width,self.options.height);
          self.renderer.paper = self._$widget_renderer;
          self.renderer.element = self._$c;
       	  if(self.renderer.init != null)
       		  self.renderer.init()
        }
        else
          logging.error('no widget renderer for '+t)
      }
      else
      {
        self._$c.empty();
      }
  },
  value:function(wi)
  {//we use this to keep track of the updated rect for a widget cell//
	var self = this;
    if (wi==null)
      return self._data;
    self._data = wi;

  },
  data:function(wi)
  {

	var self = this;
    if (wi==null)
      return self._data;

    self._data = wi;
    var t = wi.widget.key_name;
    self.key(wi.key)
    self.type(t);
    self.state(wi.state)
    if (self._$state!=null)
    	self._$state.empty().append(widget_state_to_str(self.state()));
    if (self._$action_text!=null && self._data.props['action-text']!=null && self._data.props['action-text']!='')
    	self._$action_text.empty().append('<a href="'+self._data.props['action-link']+'" target="_blank">'+self._data.props['action-text']+'</a>');
    if ($.widget_registry!=null)
    	self.definition($.widget_registry.get_by_key(self._data.widget.key_name));

    switch (self.state())
    {
	  case WIDGET_SERVICE_STATE_OK:
	  case WIDGET_STATE_CORRELATION_OK:
	    if(!self.in_config)
	    {
	      self._hide_props();
	      break;
	    }
	  default:
	    self._show_props();
    }
  },

  type: function(t)
  {
    var self = this;
    if (t==null)
      return self._type;

    self._type = t;
  },
  dashboard: function(d)
  {
	var self = this, el = this.element;
  	if (d==null)
    	 return self._dashboard;

    self._dashboard = d;
  },
  definition: function(t)
  {

  	var self = this, el = this.element;
  	if (t==null)
    	 return self._definition;

    self._definition = t;
  },
  key: function(t)
  {
  var self = this, el = this.element;
  if (t==null)
     return self._key;

    self._key = t;
  },

  state: function(s)
  {
    var self = this, el = this.element;
    if (s==null)
      return self._state;
    self._state = s;
  },
  
  updateProps: function(v)
  {
    var self = this, el = $(this.element);
    if (self._$props!=null && self._$back!=null)
    {
    	var h = self._$props.height() +80;
        self._$back.css({'height':h+'px'});
        el.css({'z-index': 11});
    }
  },

  size: function(t)
  {
    var self = this;
    if (t==null)
      return self._size;

    self.options.width = t[0];
    self.options.height = t[1];
    self.update_size();
  },

  update_size: function()
  {
    var self = this;

    var innerw = self.options.width - 20;
    var innerh = self.options.height - 50;

    self._$c.css({'width':innerw+'px','height':innerh+'px'})
    self._$p.css({'width':innerw+'px','height':innerh+'px'})
    if (self._$widget_renderer!=null)
      self._$widget_renderer.setSize(innerw,innerh);
    if (self.renderer!=null)
      self.renderer.setSize(innerw,innerh);
    if (self._$back!=null)
      self._$back.css({'width':self.options.width+'px'})

  },

  _enter_config:function()
  {
    var self = this;
    self.in_config = true;
    self._show_props()
  },

  _exit_config:function()
  {
    var self = this, el = this.element;
    self.in_config = false;
    var self = this;
    var qs = $('#'+self.key()+"-props-form").serialize();
    $.get_json('/api/dashboard/update_props_for_widget/'+self.key()+"?"+qs, null, function(wi)
    {
        $.get_json('/api/dashboard/refresh_widget_data/'+self.key(), null, function(wi)
        {
        ////  el.css({'-webkit-transform': 'rotateY(0deg)'});
          self.data(wi);
        });
    });
  },
  _show_props: function()
  {
  //build config form
  var self = this, el = this.element;
  //// el.css({'-webkit-transform': 'rotateY(180deg)'});

  switch(self.state())
    {
    case WIDGET_STATE_REQUIRES_AUTH:
      self._add_connection_props();
      break;
    case WIDGET_STATE_REQUIRES_SERVICE_SELECTION:
      self._add_connection_props();
      self._add_selector_props();
      self._add_props_form();
      self._add_done_button();
      break;
    case WIDGET_STATE_READY:
    case WIDGET_SERVICE_STATE_OK:
      self._add_connection_props();
      self._add_selector_props();
      self._add_props_form();
      self._add_done_button();
      break;
   case WIDGET_STATE_CORRELATION_REQUIRES_MEMBERS:
   	    self._$props_container.empty()
   		if(self._$done != null)
   		{
   			self._$done.remove();
   		}
   		self._add_correlation_members()
   		break;
   case WIDGET_STATE_CORRELATION_READY:
   case WIDGET_STATE_CORRELATION_OK:
   		self._add_correlation_members()
   	 	self._add_props_form();
      	self._add_done_button();
   		break;
   case WIDGET_STATE_CORRELATION_ERROR:
   case WIDGET_STATE_CORRELATION_FAILED_500:
  	 break;
    default:
      self._$state.empty().append("Widget in Bad Config State "+widget_state_to_str(self.state()));
    }

    self._$front.hide();
    self._$back.show();
	self._$back.scrollTop(0);
  },

  _hide_props: function()
  {
    var self = this;
    switch(self.state())
    {

    case WIDGET_SERVICE_STATE_OK:
    case WIDGET_STATE_CORRELATION_OK:
     self._add_renderer();
      $.get_json('/api/dashboard/get_data_for_widget/'+self.key(), null, function(data) {
            if (self.renderer!=null)
            {
			   self.data()['data'] = data;
			   self.renderer.setValue(self.data());
            }
            self.size([self.options.width,self.options.height])
          });
    }

    if (self._$back!=null)
    	self._$back.hide();
    self._$front.show();
  },
  in_edit_mode: function()
  {
	  var self = this;
	  return self._$back.is(':visible');
  },
  _update_connection: function(e)
  {
    var self = this;
    var idx      = e.data.idx
    var conn_key = self._$connection_selects[idx].val();
    var args     = self._data.key+'/'+idx+'/'+conn_key;
    $.get_json('/api/dashboard/set_connection_for_widget/'+args, null, function(wi){
       self.data(wi);
    });
  },

  _update_selector: function(selector_name)
  {
    var self = this;
    var selector        = self._$selectors[selector_name];
    var selector_value  = selector.val();
    var args            = self._data.key+'/'+selector_name+'/'+encodeURIComponent(selector_value)+'/';
    if (selector.get_selected_display_value!=null)
    	args 		   += encodeURIComponent(selector.get_selected_display_value())
    else
    	args 		   += encodeURIComponent(selector_value);
    $.get_json('/api/dashboard/set_selector_value_for_widget/'+args, null, function(wi){
      self.data(wi);
    });
  },
  _refresh_widget_data: function(e)
  {
    var self = this;
    $.get_json('/api/dashboard/refresh_widget_data/'+self._data.key, null, function(wi){
      self.data(wi);
    });
  }


});



/**
 *  dashboard tabs (with add button)
 */


$.widget( "impact.dashboard_tabs",
{

  _create: function()
  {
    var self = this, el = this.element;
    self._$clist = $(el).clist({
      add:              function(e,ui){ self._add() },
      deletef:          function(e,ui){ self._delete(ui.index); },
      select:           function(e,ui){ self._trigger('select',0,{index: ui.index}) },
      cell_renderer:    function(d){ return $("<div class='dashboard-tab rounded-corners-top'><span class='click_area'>"+d.name+"</span></div>") },
      deletable:        false,
      selectable:       true
    })
    //self._$clist._$dupe = $("<div style='float:right'>Duplicate</div>");
    //self._$clist._$dupe.click(function(){  self._dupe() });
    //self._$clist.append(self._$clist._$dupe);
  },

  value: function(t)
  {
    var self = this, el = this.element;
    if (t==null)
      return self._$clist.clist('value');
    self._$clist.clist('value',t);
  },

  select: function(index)
  {
    var self = this, el = this.element;
    self._$clist.clist('select',index);
  },

  _add: function()
  {
    var self = this, el = this.element;
    $.dialog_input('Add Dashboard','Name','Ok','Cancel',
      function(name) {
        $.get_json('/api/dashboard/add/'+name, null, function(data) {
          self._$clist.clist('add_value',data);
          var idx = self.value().length-1;
          self.select(idx);
          self._trigger('select',0,{index: idx});
        });
      });
  },

  _dupe: function()
  {
    var self = this, el = this.element;
    var value = self.value();
    var idx = self._$clist.clist('selection_index');
    $.dialog_input('Duplicate Dashboard','Name','Ok','Cancel',
      function(name) {
        $.get_json('/api/dashboard/duplicate/'+value[idx].key+"/"+name, null, function(data) {
          self._$clist.clist('add_value',data);
          var idx = self.value().length-1;
          self.select(idx);
          self._trigger('select',0,{index: idx});
        });
      });
  },

  _delete: function(idx)
  {
    var self = this, el = this.element;
    var value = self.value();
    $.dialog_ok('Really','Are you sure you want to delete the dashboard '+value[idx].name+'?','Ok','Cancel',
      function() {
        $.get_json('/api/dashboard/delete/'+value[idx].key, null, function(data) {
           self._$clist.clist('remove',idx);
           idx--;
           if (idx<0) idx=0;
           self.select(idx);
           self._trigger('select',0,{index: idx});
        });
      });
  }


});








$.widget( "impact.widget_selector",
{

  _create: function()
  {
    var self = this, el = this.element;
    self.lis = el.find("ul li");
    self.lis.each(function(i){ self._init_li($(self.lis[i]), i); })
    self.panels = el.find(".widget-category");
    self.panels.each(function(i){ self._init_panel($(self.panels[i])); })
    self.select(0);
  },

  _init_li: function($li,index)
  {
     var self = this, el = this.element;
     $li.addClass('widget-category-tab');
     $li.click(function(){
    	 self.select(index);
    	 hash = $('a', this).attr('href');
    	 hash = hash.replace(/^.*#/, '');
    	 hash = hash.replace('widgets-', '');
    	 bgColor = '';
    	 if (hash=='vimeo') {
    		 bgColor = '#F7CA3B'
    	 }
    	 if (hash=='twitter') {
    		 bgColor = '#4ABFDF'
    	 }
    	 if (hash=='facebook') {
    		 bgColor = '#558CC2'
    	 }
    	 if (hash=='custom') {
    		 bgColor = '#FFAE55'
    	 }
    	 if (hash=='google_analytics') {
    		 bgColor = '#A7CD43'
    	 }
    	 if (hash=='you_tube') {
    		 bgColor = '#FC7956'
    	 }
    	 if (hash=='correlation') {
    		 bgColor = '#FFAE55'
    	 }

    	 p = $(this).parents('#widget-tabs-wrapper');
    	 $('li', p).css('background-color', 'transparent');
    	 $(this).css('background-color', bgColor);
    	 $(this).parents('#widget-tabs-wrapper').css('border-bottom-color', bgColor);
    	 return false;
	 });
  },

  _init_panel: function($panel)
  {
    $panel.css('display','none');
  },

  select: function(index)
  {
    var self = this, $panel = $(self.panels[index]), $li = $(self.lis[index]);
    if (self._$last_panel!=null)
      self._$last_panel.css('display','none');
    $panel.css('display','block');
    self._$last_panel = $panel;
    if (self._$last_li!=null)
      self._$last_li.removeClass('selected');
    $li.addClass('selected');
    self._$last_li = $li;
  }



});




/* wrapper functions for correlation property view */

$$correlation_member = function(text,class_name)
{
    var $c = $("<div style='width:104px;height:104px;margin:5px;background-color:#f8f8f8;' class='rounded-corners-small'></div>");
    var stripe = $c.append("<div style='background-color:#fc7956;height:10px;' class='rounded-corners-top' ></div>");
    var text   = $c.append("<div style='margin:10px;font-size:8px;'>"+text+"</div>");
    if (class_name!=null)
      $c.addClass(class_name)
    return $c;
}

$$correlation_slug = function(class_name)
{
    var $c = $("<div style='width:104px;height:104px;margin:5px;background-color:#aaaaaa;' class='rounded-corners-small'></div>");
    if (class_name!=null)
      $c.addClass(class_name)
    return $c;
}

$$clear_both = function()
{
    var $c = $("<br style='clear_both'/>");
    return $c;
}
/* end wrapper functions */

